package com.ve.clexer;

import com.ve.clexer.tree.Function;
import com.ve.clexer.tree.RangeNode;
import com.ve.clexer.tree.TreeBuilder;
import com.ve.clexer.tree.Type;
import com.ve.clexer.tree.Variable;

import java.util.ArrayList;
import java.util.Stack;


public class CoreLexer {
    protected Stack<Status> statusStack;
    protected Status currentStatus;
//    protected Word.Analyzer wordAnalyzer;

    protected ArrayList<Word> words;
    protected Word word, nameWord;

    protected TreeBuilder tree;
    private ContentSeeker seeker;

    private boolean functionFlag;
    private SentenceLexer sentenceLexer;
    private BraceMatcher braceMatcher;

    public CoreLexer(CharSequence lexer) {
        tree = new TreeBuilder();
        statusStack = new Stack<>();
        words = new ArrayList<>(64);
//        wordAnalyzer = new Word.Analyzer();
        changeStatus(Status.DOCUMENT);
        seeker =new ContentSeeker(lexer);
        braceMatcher=new BraceMatcher();

    }


    public void setSentenceLexer(SentenceLexer sentenceLexer) {
        this.sentenceLexer = sentenceLexer;
    }

    public void lex(RangeNode node) {
        tree.seek(node);
        functionFlag = false;
        seeker.begin(this::onLexing);
    }



    private void lexBodyStart(Status targetStatus) {


        int c=seeker.currentChar();
        switch (c) {
            case '{':
                changeStatus(targetStatus);
                sentenceEnd();
                statusStack.push(targetStatus);
                changeStatus(targetStatus);
                break;
            case ';':
                sentenceEnd();
                break;

        }
    }

    private void lexBodyEnd() {
        if (braceMatcher.listen(BraceMatcher.BRACE_BIG,seeker.currentChar())) {
            endBody();
        }
    }
    private void onLexing(char c,char lastc,char nc,char nlastc) {
        switch (currentStatus) {
            case DOCUMENT:
                lexWord();
                break;
            case FUNCTION_OR_VARIABLE_DEFINE:
                addAnalyzeWord();
                switch (c) {
                    case '(':
                        functionFlag =true;
                        break;
                    case '=':
                        if (!functionFlag) {
                            changeStatus(Status.VARIABLE_DEFINE);
                        }
                        break;
                    case '{':
                        changeStatus(Status.FUNCTION_BODY);
                        sentenceEnd();
                        statusStack.push(Status.FUNCTION_BODY);
                        break;
                    case ';':
                        changeStatus(functionFlag ? Status.FUNCTION_DEFINE : Status.VARIABLE_DEFINE);
                        sentenceEnd();
                        break;
                }


                if (nlastc == '(' && nc == '*') {
                    //函数指针
                    changeStatus(Status.VARIABLE_DEFINE);
                    break;
                }
                break;
            case FUNCTION_DEFINE:
                addAnalyzeWord();
                lexBodyStart(Status.FUNCTION_BODY);
                break;
            case FUNCTION_BODY:
                lexFunctionBody();
                break;
            case VARIABLE_DEFINE:
                addAnalyzeWord();
                lexBodyStart(Status.VARIABLE_BODY);
                break;
            case VARIABLE_BODY:
                lexVariableBody();
                break;
            case CLASS_DEFINE:
                addAnalyzeWord();
                lexBodyStart(Status.CLASS_BODY);
                break;

            case CLASS_BODY:
                lexClassBody();
                // can't reach
                break;
            case CLASS_LIMIT:
                break;
            case ENUM_DEFINE:

                addAnalyzeWord();
                lexBodyStart(Status.ENUM_BODY);
                break;
            case ENUM_BODY:

                lexEnumBody();
                break;

            case ANNOTATION_SINGLELINE:
                if (c == CLanguage.NEWLINE) {
                    sentenceEnd();
                }
                break;
            case ANNOTATION_MULTILINE:
                if (lastc == '*' && c == '/') {
                    sentenceEnd();
                }
                break;
            case PRETREATMENT:
                if (c == CLanguage.NEWLINE) {
                    sentenceEnd();
                } else {
                    addAnalyzeWord();
                }
                break;

            case TYPEDEF:
                addAnalyzeWord();
                if (c == CLanguage.NEWLINE) {
                    sentenceEnd();
                }
                break;
            case UNION_DEFINE:
                addAnalyzeWord();
                lexBodyStart(Status.UNION_BODY);

                break;
            case UNION_BODY:
                lexClassBody();
                break;
            case USING:
                addAnalyzeWord();
                if (c == CLanguage.NEWLINE) {
                    sentenceEnd();
                }
                break;
            case OPERATING:
                addAnalyzeWord();
                break;
        }
    }

    private void lexEnumBody() {
        if (seeker.currentChar() == '}') {
            sentenceEnd();
            addAnalyzeWord();
            lexBodyEnd();
        } else {
            lexChar();
        }

    }


    private void addAnalyzeWord() {
        //addWord(wordAnalyzer.analyse(seeker.getIndex(), seeker.getRow(), seeker.currentChar()));
    }


    private void lexNamespaceBody() {
        lexWord();
        lexBodyEnd();
    }

    private void lexClassBody() {
        lexWord();
        lexBodyEnd();
        /*if (seeker.currentChar() == ':' && nameWord != null && nameWord.isClassLimitWord()) {
            changeStatus(Status.CLASS_LIMIT);
            sentenceEnd();
        }*/
    }

    private void lexVariableBody() {
        if (seeker.currentChar() == '}') {
            sentenceEnd();
            addAnalyzeWord();
            lexBodyEnd();
        } else {
            lexChar();
        }
    }

    private void lexFunctionBody() {
        lexWord();
        lexBodyEnd();
        if (seeker.currentChar() == ';') {
            changeStatus(Status.OPERATING);
            sentenceEnd();
        }
    }


    private void lexChar() {
        addAnalyzeWord();
        char c=seeker.currentChar();
        char lastc=seeker.lastChar();
        switch (c) {
            case '#':
                changeStatus(Status.PRETREATMENT);
                break;
            case '/':
                if (lastc == '/') {
                    changeStatus(Status.ANNOTATION_SINGLELINE);
                }
                break;
            case '*':
                if (lastc == '/') {
                    changeStatus(Status.ANNOTATION_MULTILINE);
                }
                break;
            case ';':

                changeStatus(Status.OPERATING);
                sentenceEnd();
                break;

        }

    }

    private void lexWord() {
        lexChar();
/*
        if (word != null && word.is(Word.NANE)) {

            if (word.equals("typedef")) {
                changeStatus(Status.TYPEDEF);
            } else if (word.equals("template")) {
                changeStatus(Status.TEMPLATE);
            } else if (word.equals("class")) {
                changeStatus(Status.CLASS_DEFINE);
            } else if (word.equals("struct")) {
                changeStatus(Status.CLASS_DEFINE);
            } else if (word.equals("union")) {
                changeStatus(Status.UNION_DEFINE);
            } else if (word.equals("enum")) {
                changeStatus(Status.ENUM_DEFINE);
            } else if (word.equals("using")) {
                changeStatus(Status.USING);
            } else {
                Type type = tree.findType(word.getData());
                if (type != null) {
                    changeStatus(Status.FUNCTION_OR_VARIABLE_DEFINE);
                } else {
                    System.out.println("unknow word " + word.getData());
                }
            }
        }
*/


    }

    private void changeStatus(Status status) {
        this.currentStatus = status;
    }

    public int getPosition() {
        return words.size() > 0 ? words.get(0).getPosition() : 0;
    }

    private void sentenceEnd() {
//        addWord(wordAnalyzer.fflush());
        lexSentence();
        words.clear();
        word = null;
        nameWord = null;
        currentStatus = statusStack.empty() ? Status.DOCUMENT : statusStack.peek();

    }

    private void endBody() {
        if (!statusStack.empty()) {
            statusStack.pop();
        }
        changeStatus(Status.BODY_END);
        sentenceEnd();
    }

/*    private void addWord(Word word) {
        if (word != null) {
            if (word.is(Word.NANE)) {
                nameWord = word;
            }
            words.add(this.word = word);
        }

    }*/

    private String getStatusText() {
        StringBuilder sb = new StringBuilder();
        for (Status status : statusStack) {
            sb.append(status).append("/");
        }
        return sb.toString() + currentStatus;
    }

    private void lexSentence() {
        System.out.println("[" + seeker.getRow() + "][" + getStatusText() + "]  ▎" + this);
        lexStatus(currentStatus);
        if (sentenceLexer != null) {
            sentenceLexer.onLex(currentStatus,tree,braceMatcher,words);
        }
    }

    private void lexStatus(Status status) {
/*
        switch (status) {
            case DOCUMENT:
                break;
            case FUNCTION_DEFINE:
            case FUNCTION_BODY:
                Function fun = new Function();
                Type type = null;
                Word word;
                Variable parameter;
                int i = 0;
                for (; i < words.size(); ++i) {
                    word = words.get(i);
                    if (word.is(Word.NANE)) {
                        type = tree.findType(word.getData());
                        if (type != null) {
                            break;
                        }
                    }
                }
                if (type != null) {
                    fun.returnType = new Variable.VariableType(type);
                }
                for (; i < words.size(); ++i) {
                    word = words.get(i);
                    if (word.equals("(")) {
                        i++;
                        break;
                    } else {
                        if (word.is(Word.NANE)) {
                            fun.name = word.getData();
                        }
                    }
                }

                for (; i < words.size(); ++i) {
                    word = words.get(i);
                    if (word.equals(")")) {
                        break;
                    } else {
                        if (fun.parameters.isEmpty()) {
                            parameter = new Variable();
                            parameter.name = word.getData();
                        }
                    }
                }

                tree.addFunction(fun);

                break;
            case VARIABLE_DEFINE:

                break;
            case CLASS_DEFINE:
                break;

            case CLASS_BODY:
                break;
            case ENUM_DEFINE:
                break;
            case ENUM_BODY:
                break;

            case CLASS_LIMIT:
                break;
            case ANNOTATION_SINGLELINE:

                break;
            case ANNOTATION_MULTILINE:

                break;
            case PRETREATMENT:
                break;

            case TYPEDEF:

                break;
            case UNION_DEFINE:
                break;
            case UNION_BODY:

                break;
            case USING:
                break;
            case OPERATING:

                break;
        }
*/
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (Word w : words) {
            sb.append(w).append(' ');
        }
        return sb.toString();
    }

    public enum Status {
        DOCUMENT,
        FUNCTION_OR_VARIABLE_DEFINE,
        TEMPLATE,

        VARIABLE_DEFINE,
        VARIABLE_BODY,

        FUNCTION_DEFINE,
        FUNCTION_BODY,

        CLASS_DEFINE,
        CLASS_BODY,
        CLASS_LIMIT,

        ENUM_DEFINE,
        ENUM_BODY,


        UNION_DEFINE,
        UNION_BODY,

        ANNOTATION_SINGLELINE,
        ANNOTATION_MULTILINE,

        PRETREATMENT,

        BODY_END,
        TYPEDEF,

        USING,

        OPERATING,

    }

}
